Obsidian Relation Renovation
Obsidian Local Graph를 깔끔하게 보기 위해선 그래프가 트리구조를 가져야 한다. 즉, 순환이 없어야 한다. Index 폴더의 파일들을 각각의 루트로 보았을 때 자식 문서들로 향하는 유향그래프를 그렸을 때 parent link로 향하는 간선이 존재해서는 안된다.
이런 제약조건을 걸면 뭐가 좋냐고? 로컬 그래프에 Incoming links와 Outgoing links를 켜고 끌 수 있다. 나가는 참조와 들어오는 참조를 분리하면 지식을 찾는데 잡음을 덜 수 있을 것이다.
물론 예외는 분명히 있어야한다. 대표적으로 Daily Notes. Daily Routine 때문에 모든 데일리 노트들이 인덱스의 문서를 참조하고 있어 이건 필터로 처리해주는 것이 좋을 듯 하다.
2025-02-11
부모가 자식을 레퍼런스하는 것보다 자식이 부모를 레퍼런스 하는 것이 더 자연스럽다. 하지만 인덱스 페이지는 필요한데? -> 태그를 활용하면 된다. 자식 페이지들은 전부 인덱스 태그를 가지고 인덱스 페이지들은 두 set을 합집합으로 쿼리하여 표현하면 완전한 트리구조를 구현할 수 있을 것이다.
여기서 말하는 두 Set은 다음과 같다:
- Tag
- Backlink (including frontmatter
links:
)
2025-02-20
인덱스 페이지도 웰컴 페이지와 똑같이 PARA 구조를 띄게 하면 어떨까? 쉽게 보면 웰컴페이지에서 각 인덱스의 주제와 연관된 문서들만을 필터링 하는 것이다. 0180 Prisma 🌈에서 실험해보겠다.
2025-02-22
2025-02-20 실험결과, 주제와 무관한 문서들이 쿼리되기 시작했다. 단순 백링크를 조회하는 기능은 이미 Digital Garden에서 옵션이 제공이 되기 때문에 그 친구한테 맞기고 다른 방식으로 자식 문서들을 분류해야 할 것으로 보인다.
한 가지 아이디어는 태그를 활용하는 것이다. 태그도 계층구조가 가능하며, 굳이 폴더로 물리적인 분리를 하지 않더라도 논리적인 분리가 가능해진다. 태그로 dataview 그룹화를 하는 것이다. 그러면 같은 계층에 포함된 문서들은 같은 depth에 묶일 것이다. 이번엔 0200 mongodb 🥬에서 실험해보겠다.
흐으으으으으으으음.... dv.list
는 indentation을 지원하지 않아 직접
를 추가할 수밖에 없었다. 아래는 위의 렌더링을 만들어낸 dataviewjs 스크립트다:
let pages = dv.pages("")
.where(p => p.file.outlinks.includes(dv.current().file.link)) // Filter documents linking to this one
.filter(p => p.tags && p.tags.length > 0); // Ensure they have tags
let tagTree = {};
// Organize documents into a nested tag structure
pages.forEach(page => {
page.tags.forEach(tag => {
let parts = tag.split("/"); // Split tag by "/"
let rootTag = parts[0]; // First part of tag (e.g., #mongodb from #mongodb/migration)
let subPath = parts.slice(1).join("/"); // Everything after root (e.g., migration)
if (!tagTree[rootTag]) tagTree[rootTag] = {}; // Ensure root exists
let currentLevel = tagTree[rootTag];
if (subPath) {
let subParts = subPath.split("/");
subParts.forEach((sub, index) => {
if (!currentLevel[sub]) currentLevel[sub] = {}; // Create subtags if missing
if (index === subParts.length - 1) {
if (!currentLevel[sub].docs) currentLevel[sub].docs = [];
currentLevel[sub].docs.push(page);
}
currentLevel = currentLevel[sub]; // Move deeper into the structure
});
} else {
if (!currentLevel.docs) currentLevel.docs = [];
currentLevel.docs.push(page);
}
});
});
function indent(count = 0) {
return ' '.repeat(count * 8);
}
// Recursive function to render tree structure
function renderTree(tree, depth = 0) {
for (let [tag, value] of Object.entries(tree)) {
if (tag !== "docs") {
// Render tag
dv.paragraph(`${indent(depth)} #${tag}`);
// Recursively render subtags
renderTree(value, depth + 1);
} else {
// Render documents
dv.list(value.map((doc) => `${indent(depth)} ${doc.file.link}`));
}
}
}
// Render the full tag tree
renderTree(tagTree);